﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Runtime.Serialization;
using PowerTriggers.ServiceReference;

namespace PowerTriggers
{
    internal partial class EditTasksUserControl : UserControl
    {
        public event EventHandler<SelectedTasksEventArgs> TasksSelected;
        public event EventHandler OtherClicked;

        private IDictionary<string, Task> availableTasks;
        private IList<Task> filteredAvailableTasks;
        private IList<Task> selectedTasks;
        private bool showCategories;

        public EditTasksUserControl()
        {
            InitializeComponent();
            foreach (Control control in this.Controls.OfType<Button>())
            {
                control.Enabled = Program.IsCurrentUserAdministrator;
            }
        }

        public bool SupportsOther
        {
            get
            {
                return DefaultProperties.Where(prop => prop.Name == TaskPropertyNames.SupportsOther)
                                        .SelectMany(prop => prop.Choices)
                                        .Any(choice => choice.ChoiceType == TaskPropertyChoiceTypes.Yes);
            }
        }

        internal TaskTypes TaskType
        {
            get;
            set;
        }

        internal IList<TaskProperty> DefaultProperties;

        public void Create(TaskTypes taskType, IDictionary<TaskTypes, IList<TaskProperty>> defaultProperties, IList<Task> selectedTasks)
        {
            this.TaskType = taskType;
            if (defaultProperties.ContainsKey(taskType))
            {
                this.DefaultProperties = defaultProperties[taskType];
            }
            else
            {
                this.DefaultProperties = new List<TaskProperty>();
            }
            this.selectedTasks = selectedTasks.Where(t => t.TaskType == taskType).ToList();
        }

        internal void LoadData()
        {
            if (this.availableTasks == null)
            {
                var savedCursor = Cursor.Current;
                try
                {
                    Cursor.Current = Cursors.WaitCursor;
                    this.availableTasks = GetAllAvailableTasks().OrderBy(t => t.Description).ToDictionary(t => t.ID.ToUpper(), t => t);

                    // The selectedTasks list may be replaced with the instances defined in the availableTasks
                    // parameter.  This is so that when comparing the instances in equality comparisons they will match
                    bool supportsOther = SupportsOther;
                    foreach (Task task in this.selectedTasks.Where(t => t.TaskType == TaskType).ToList())
                    {
                        if (!this.availableTasks.ContainsKey(task.ID.ToUpper()))
                        {
                            this.selectedTasks.Remove(task); // this should never happen, but do it just in case
                            EventLogHelper.LogWarning(string.Format("Found unused task in selected tasks list. TaskType={0}, ID={1}", task.TaskType, task.ID));
                        }
                        else
                        {
                            Task availableTask = this.availableTasks[task.ID.ToUpper()];
                            availableTask.Sequence = task.Sequence;
                            availableTask.Properties = task.Properties;
                            this.selectedTasks[this.selectedTasks.IndexOf(task)] = availableTask;
                        }
                    }

                    string[] categories = this.availableTasks.Select(kvp => kvp.Value.Category ?? string.Empty).Distinct().Where(cat => cat.Length > 0).OrderBy(cat => cat).ToArray();
                    showCategories = categories.Length > 1;

                    base.OnResize(EventArgs.Empty);

                    if (showCategories)
                    {
                        comboBoxCategory.Items.Add("(All)");
                        comboBoxCategory.Items.AddRange(categories);
                        comboBoxCategory.SelectedIndex = 0;
                        comboBoxCategory.Visible = true;
                        labelCategory.Visible = true;
                    }
                    else
                    {
                        comboBoxCategory.Visible = false;
                        labelCategory.Visible = false;
                    }

                    RefreshList();
                }
                finally
                {
                    Cursor.Current = savedCursor;
                }
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        private IList<Task> GetAllAvailableTasks()
        {
            try
            {
                using (PowerTriggersWcfClient serviceClient = WcfClientFactory.OpenWcfClient())
                {
                    return serviceClient.GetAllAvailableTasks(TaskType);
                }
            }
            catch (System.Exception ex)
            {
                Cursor.Current = Cursors.Default;
                MessageBox.Show(this, "Error occurred retrieving list from server: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return new List<Task>(this.selectedTasks.Where(t => t.TaskType == TaskType));
            }
        }

        internal void Include(Task task)
        {
            if (task.TaskType == this.TaskType)
            {
                LoadData();
                if (this.selectedTasks.Contains(task))
                {
                    this.selectedTasks.Remove(task);
                }
                if (!this.availableTasks.ContainsKey(task.ID.ToUpper()))
                {
                    this.availableTasks.Add(task.ID.ToUpper(), task);
                    this.availableTasks = this.availableTasks.OrderBy(kvp => kvp.Value.Description).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
                }
            }
        }

        internal void RefreshList()
        {
            if (this.availableTasks != null)
            {
                UpdateFilteredTasks();

                dataGridViewNonSelect.RowCount = filteredAvailableTasks.Count;
                dataGridViewNonSelect.Refresh();
            }
            buttonOther.Visible = SupportsOther;
        }

        private void UpdateFilteredTasks()
        {
            if (showCategories && comboBoxCategory.SelectedIndex > 0)
            {
                string category = ((string)comboBoxCategory.Items[comboBoxCategory.SelectedIndex]).ToLower();

                this.filteredAvailableTasks = (from kvp in this.availableTasks
                                               where !this.selectedTasks.Any(t => t.ID.ToUpper() == kvp.Key)
                                                  && kvp.Value.Category.ToLower() == category
                                               select kvp.Value)
                        .ToList();
            }
            else
            {
                this.filteredAvailableTasks = (from kvp in this.availableTasks
                                               where !this.selectedTasks.Any(t => t.ID.ToUpper() == kvp.Key)
                                               select kvp.Value)
                        .ToList();
            }
        }

        private void EditTasksUserControl_Resize(object sender, EventArgs e)
        {
            int listWidth = (this.ClientSize.Width - buttonSelect.Width - 5);
            if (listWidth < 0)
            {
                listWidth = 0;
            }

            int comboWidth = listWidth - comboBoxCategory.Left;
            if (comboWidth < 50)
            {
                comboWidth = 50;
            }
            comboBoxCategory.Width = comboWidth;

            int top = this.showCategories ? (comboBoxCategory.Bottom + 5) : 0;
            dataGridViewNonSelect.Bounds = new Rectangle(0, top, listWidth, this.ClientSize.Height - top);
        }

        private void dataGridViewNonSelect_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            DataGridView dgv = (DataGridView)sender;

            if (e.RowIndex >= 0 && e.RowIndex < filteredAvailableTasks.Count)
            {
                DataGridViewRow dgvr = dgv.Rows[e.RowIndex];
                Task task = filteredAvailableTasks[e.RowIndex];

                e.Value = task.Description;
                dgvr.Tag = task;
            }
        }

        private void comboBoxCategory_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (showCategories)
            {
                Cursor.Current = Cursors.WaitCursor;
                dataGridViewNonSelect.Rows.Clear();
                RefreshList();
                Cursor.Current = Cursors.Default;
            }
        }

        private void buttonSelect_Click(object sender, EventArgs e)
        {
            RaiseTasksSelected();

            RefreshList();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void RaiseTasksSelected()
        {
            try
            {
                if (Program.IsCurrentUserAdministrator)
                {
                    if (TasksSelected != null)
                    {
                        IList<int> selectedIndices = (from dgvc in dataGridViewNonSelect.SelectedCells.OfType<DataGridViewCell>()
                                                      select dgvc.RowIndex).Distinct().ToList();

                        IList<Task> userSelectedTasks = new List<Task>(selectedIndices.Count);

                        foreach (int i in selectedIndices)
                        {
                            Task task = (Task)dataGridViewNonSelect.Rows[i].Tag;
                            System.Diagnostics.Debug.Assert(task != null, "No task found");
                            if (task != null)
                            {
                                userSelectedTasks.Add(task);
                                if (!this.selectedTasks.Contains(task))
                                {
                                    this.selectedTasks.Add(task);
                                }
                            }
                        }

                        if (selectedTasks.Count > 0)
                        {
                            TasksSelected(this, new SelectedTasksEventArgs(userSelectedTasks));
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(this, "Error: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void buttonOther_Click(object sender, EventArgs e)
        {
            if (OtherClicked != null)
            {
                OtherClicked(this, e);
            }
        }

        private void dataGridViewNonSelect_KeyDown(object sender, KeyEventArgs e)
        {
            // search for the next item in the list that matches the pressed key
            try
            {
                string key = e.KeyData.ToString();
                if (key.Length == 1) // is an alphabetical key
                {
                    DataGridView dgv = (DataGridView)sender;
                    int currentRowIndex = dgv.CurrentRow.Index;
                    if (currentRowIndex < 0)
                    {
                        currentRowIndex = 0;
                    }
                    DataGridViewRow nextRow = dgv.Rows
                                                .OfType<DataGridViewRow>()
                                                .Where(dgvr => dgvr.Index > currentRowIndex
                                                                && dgvr.Cells.Count > 0
                                                                && dgvr.Cells[0].Value.ToString().StartsWith(key))
                                                .FirstOrDefault();
                    if (nextRow == null) // don't find a next one so look for the first previous one
                    {
                        nextRow = dgv.Rows
                                        .OfType<DataGridViewRow>()
                                        .Where(dgvr => dgvr.Index < currentRowIndex
                                                        && dgvr.Cells.Count > 0
                                                        && dgvr.Cells[0].Value.ToString().StartsWith(key))
                                        .FirstOrDefault();
                    }
                    if (nextRow == null)
                    {
                        System.Media.SystemSounds.Beep.Play();
                    }
                    else
                    {
                        dgv.ClearSelection();
                        dgv.CurrentCell = nextRow.Cells[0];
                        nextRow.Cells[0].Selected = true;
                    }
                }
            }
            catch { }
        }

    }

    internal class SelectedTasksEventArgs : EventArgs
    {
        private SelectedTasksEventArgs() { }

        internal SelectedTasksEventArgs(IList<Task> selectedTasks)
        {
            this.SelectedTasks = selectedTasks;
        }

        internal IList<Task> SelectedTasks
        {
            get;
            private set;
        }
    }
}
